package gerror

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"log/slog"
	"strconv"
)

type IError interface {
	// Code 获取错误码
	Code() int
	// Error 获取错误信息(不包含堆栈信息)
	Error() string
	// String 获取详细的错误信息(如存在堆栈信息则包含)
	String() string
	// Unwrap 获取被包装的错误, 如果没有被包装，则返回 error<nil>
	Unwrap() error
	// WithStack 手动关闭或启用(更新)堆栈信息
	//   - enabled 为 false 时，关闭堆栈信息
	//   - enabled 为 true 时，启用(更新)堆栈信息
	//   - - !isDefined 非预定义错误对象，如不存在堆栈信息则开启堆栈，否则不做任何操作
	//   - - isDefined 预定义错误对象，创建一个错误对象的副本并启用堆栈信息
	WithStack(enabled bool) IError
	// WithLogger 手动将错误信息登记到指定的slog日志记录器
	WithLogger(logs ...*slog.Logger)
	// Source 获取Caller堆栈Source信息(第一条堆栈信息)
	//   - source 返回值: 获取成功时返回Source对象
	//   - exists 返回值: 未开启堆栈或获取失败时返回 false
	Source() (source Source, exists bool)
	// Stacks 获取Caller堆栈的[]Source信息切片
	//   - 返回值: 未开启堆栈或获取失败时返回 nil
	Stacks() []Source
	// Format FMT格式化输出接口
	Format(s fmt.State, verb rune)
	// MarshalJSON 序列化接口 encoding/json.Marshaler
	MarshalJSON() ([]byte, error)
	// UnmarshalJSON 反序列化接口 encoding/json.Unmarshaler
	UnmarshalJSON(data []byte) error
}

type Error struct {
	code      int
	msg       string
	err       error
	isDefined bool
	caller    *Caller
}

// newGerror 创建一个 Error 错误
//   - code 错误码 (default: defaultErrCode)
//   - msg 错误信息描述字符串
//   - err 包裹的错误对象 (default: nil)
//   - stackEnabled 是否开启堆栈信息(default: defaultStackEnabled)
//   - isDefined 是否为预定义错误对象
//
//go:noinline
func newGerror(code int, msg string, err error, stackEnabled, isDefined bool) IError {
	e := &Error{code: code, msg: msg, err: err, isDefined: isDefined}
	// 如果启用了默认堆栈跟踪且当前错误不是预定义错误，则尝试获取调用者堆栈信息
	if stackEnabled && !isDefined {
		e.caller = newCaller(2)
	}
	return e
}

// New 创建一个 Error 错误
//
//go:noinline
func New(msg string) IError {
	return newGerror(defaultErrCode, msg, nil, defaultStackEnabled, false)
}

// NewF 创建一个格式化消息的 Error 错误
//
//go:noinline
func NewF(format string, a ...any) IError {
	return newGerror(defaultErrCode, fmt.Sprintf(format, a...), nil, defaultStackEnabled, false)
}

// NewCode 创建一个含有Code的 Error 错误
//
//go:noinline
func NewCode(code int, msg string) IError {
	return newGerror(code, msg, nil, defaultStackEnabled, false)
}

// NewCodeF 创建一个含有Code的格式化消息的 Error 错误
//
//go:noinline
func NewCodeF(code int, format string, a ...any) IError {
	return newGerror(code, fmt.Sprintf(format, a...), nil, defaultStackEnabled, false)
}

// Errorf 创建一个 Error 错误, like fmt.Errorf
//
//go:noinline
func Errorf(format string, a ...any) IError {
	return newGerror(defaultErrCode, fmt.Sprintf(format, a...), nil, defaultStackEnabled, false)
}

// Wrap 包裹err创建一个 Error 错误
//   - err 为 nil 时, 不进行任何操作, 返回 nil
//
//go:noinline
func Wrap(err error, msg string) IError {
	if err == nil {
		return nil
	}
	return newGerror(defaultErrCode, msg, err, defaultStackEnabled, false)
}

// WrapF 包裹err创建一个格式化消息的 Error 错误
//   - err 为 nil 时, 不进行任何操作, 返回 nil
//
//go:noinline
func WrapF(err error, format string, a ...any) IError {
	if err == nil {
		return nil
	}
	return newGerror(defaultErrCode, fmt.Sprintf(format, a...), err, defaultStackEnabled, false)
}

// WrapCode 包裹err创建一个含有Code的 Error 错误
//   - err 为 nil 时, 不进行任何操作, 返回 nil
//
//go:noinline
func WrapCode(code int, err error, msg string) IError {
	if err == nil {
		return nil
	}
	return newGerror(code, msg, err, defaultStackEnabled, false)
}

// WrapCodeF 包裹err创建一个含有Code的格式化消息的 Error 错误
//   - err 为 nil 时, 不进行任何操作, 返回 nil
//
//go:noinline
func WrapCodeF(code int, err error, format string, a ...any) IError {
	if err == nil {
		return nil
	}
	return newGerror(code, fmt.Sprintf(format, a...), err, defaultStackEnabled, false)
}

// WrapOrNew 包裹err或创建一个 Error 错误
//   - err 为 nil 时，创建一个 Error 错误
//   - err 不为 nil 时，包裹err创建一个 Error 错误
//
//go:noinline
func WrapOrNew(err error, msg string) IError {
	return newGerror(defaultErrCode, msg, err, defaultStackEnabled, false)
}

// WrapOrNewF 包裹err或创建一个格式化消息的 Error 错误
//   - err 为 nil 时，创建一个 Error 错误
//   - err 不为 nil 时，包裹err创建一个 Error 错误
//
//go:noinline
func WrapOrNewF(err error, format string, a ...any) IError {
	return newGerror(defaultErrCode, fmt.Sprintf(format, a...), err, defaultStackEnabled, false)
}

// WrapOrNewCode 包裹err或创建一个含有Code的 Error 错误
//   - err 为 nil 时，创建一个 Error 错误
//   - err 不为 nil 时，包裹err创建一个 Error 错误
//
//go:noinline
func WrapOrNewCode(code int, err error, msg string) IError {
	return newGerror(code, msg, err, defaultStackEnabled, false)
}

// WrapOrNewCodeF 包裹err或创建一个含有Code的格式化消息的 Error 错误
//   - err 为 nil 时，创建一个 Error 错误
//   - err 不为 nil 时，包裹err创建一个 Error 错误
//
//go:noinline
func WrapOrNewCodeF(code int, err error, format string, a ...any) IError {
	return newGerror(code, fmt.Sprintf(format, a...), err, defaultStackEnabled, false)
}

// Code 获取错误码
func (e *Error) Code() int {
	return e.code
}

// Error 获取错误信息(不包含堆栈信息)
func (e *Error) Error() string {
	if e.err == nil {
		if e.code != defaultErrCode {
			return fmt.Sprintf("[%d]%s", e.code, e.msg)
		}
		return e.msg
	}
	if e.code != defaultErrCode {
		return fmt.Sprintf("[%d]%s:%s", e.code, e.msg, e.err.Error())
	}
	return fmt.Sprintf("%s:%s", e.msg, e.err.Error())
}

// String 获取详细的错误信息(如有堆栈信息则包含)
func (e *Error) String() string {
	var buf bytes.Buffer
	if e.code != defaultErrCode {
		buf.WriteString(fmt.Sprintf("[%d]", e.code))
	}
	if e.msg != "" {
		buf.WriteString(e.msg)
	}
	if stacks := e.Stacks(); stacks != nil {
		for i, stack := range stacks {
			if buf.Len() > 0 {
				buf.WriteString("\n\t")
			}
			buf.WriteString(fmt.Sprintf("[stack#%d] %s %s:%d", i, stack.Func, stack.File, stack.Line))
		}
	}
	if e.err != nil {
		if buf.Len() > 0 {
			buf.WriteString("\n\t")
		}
		if ei, ok := e.err.(interface {
			String() string
		}); ok {
			buf.WriteString(ei.String())
		} else {
			buf.WriteString(e.err.Error())
		}
	}
	return buf.String()
}

// WithLogger 手动将错误信息登记到指定的slog日志记录器
func (e *Error) WithLogger(logs ...*slog.Logger) {
	if len(logs) > 0 {
		for _, log := range logs {
			if log == nil {
				continue
			}
			log.Error(e.msg, e.logRecordCause(false, e)...)
		}
	}
}
func (e *Error) logRecordCause(isMsg bool, cause error) []any {
	err, ok := cause.(*Error)
	if !ok {
		return []any{slog.String("msg", cause.Error())}
	}
	slogAttrs := []any{}
	if isMsg {
		slogAttrs = append(slogAttrs, slog.String("msg", err.msg))
	}
	if err.code != defaultErrCode {
		slogAttrs = append(slogAttrs, slog.Int("code", err.code))
	}
	if stacks := err.Stacks(); stacks != nil {
		stackGroups := []any{}
		for i, source := range stacks {
			stackGroups = append(stackGroups, slog.Group(strconv.Itoa(i),
				slog.String("file", source.File),
				slog.String("func", source.Func),
				slog.Int("line", source.Line),
			))
		}
		slogAttrs = append(slogAttrs, slog.Group("stacks", stackGroups...))
	}
	if err.err != nil {
		slogAttrs = append(slogAttrs, slog.Group("cause", e.logRecordCause(true, err.err)...))
	}
	return slogAttrs
}

// WithStack 关闭或启用(更新)堆栈信息
//   - enabled 为 false 时，强制关闭堆栈信息
//   - enabled 为 true 时，启用(更新)堆栈信息
//   - - !isDefined 非预定义错误对象，如不存在堆栈信息则开启堆栈，否则不做任何操作
//   - - isDefined 预定义错误对象，创建一个错误对象的副本并启用堆栈信息
func (e *Error) WithStack(enabled bool) IError {
	if !enabled {
		e.caller = nil
		return e
	}
	// 预定义错误码的对象，创建一个错误对象的副本，从而不影响预定义对象
	if e.isDefined {
		e = &Error{code: e.code, msg: e.msg, err: e.err, caller: newCaller(1)}
	} else if e.caller == nil {
		e.caller = newCaller(1)
	}
	return e
}

// Source 获取Caller堆栈Source信息(第一条堆栈信息)
//   - source 返回值: 获取成功时返回Source对象
//   - exists 返回值: 未开启堆栈或获取失败时返回 false

func (e *Error) Source() (source Source, exists bool) {
	return e.caller.Source()
}

// Stacks 获取Caller堆栈的[]Source信息切片
//   - 返回值: 未开启堆栈或获取失败时返回 nil
func (e *Error) Stacks() []Source {
	if e.caller == nil {
		return nil
	}
	return e.caller.Stacks()
}

// Format FMT格式化输出接口
func (e *Error) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		if s.Flag('+') {
			if e.code != defaultErrCode {
				io.WriteString(s, fmt.Sprintf("[%d]", e.code))
			}
			io.WriteString(s, e.msg)
			if e.caller != nil {
				fmt.Fprintf(s, "%+v", e.caller)
			}
			if e.err != nil {
				io.WriteString(s, "\n")
				fmt.Fprintf(s, "%+v", e.err)
			}
			return
		}
		fallthrough
	case 's':
		io.WriteString(s, e.Error())
	case 'q':
		fmt.Fprintf(s, "%q", e.Error())
	case 'd':
		io.WriteString(s, strconv.Itoa(e.code))
	}
}

// Unwrap 获取被包装的错误(支持Is/As接口), 如果没有被包装，则返回 error<nil>
func (e *Error) Unwrap() error {
	return e.err
}

type errorMarshal struct {
	Code   int           `json:"code"`
	Msg    string        `json:"msg"`
	Stacks []Source      `json:"stacks,omitempty"`
	Cause  *errorMarshal `json:"cause,omitempty"`
}

// MarshalJSON 序列化接口 encoding/json.Marshaler
//   - 注意: 序列化时, 指针信息无效做丢弃处理
func (e *Error) MarshalJSON() ([]byte, error) {
	data := &errorMarshal{
		Code:   e.code,
		Msg:    e.msg,
		Stacks: e.Stacks(),
	}
	e.errorMarshal(data, e.err)
	return packageJsoner.Marshal(&data)
}
func (e *Error) errorMarshal(errorLinked *errorMarshal, err error) {
	if err == nil {
		return
	}
	if ev, ok := err.(*Error); ok {
		errorLinked.Cause = &errorMarshal{
			Code:   ev.code,
			Msg:    ev.msg,
			Stacks: ev.Stacks(),
		}
	} else {
		errorLinked.Cause = &errorMarshal{
			Msg: err.Error(),
		}
		if errIF, ok := err.(interface {
			Code() int
		}); ok {
			errorLinked.Cause.Code = errIF.Code()
		}
		if errIF, ok := err.(interface {
			Stacks() []Source
		}); ok {
			errorLinked.Cause.Stacks = errIF.Stacks()
		}
	}
	if err := Unwrap(err); err != nil {
		e.errorMarshal(errorLinked.Cause, err)
	}
}

// UnmarshalJSON 反序列化接口 encoding/json.Unmarshaler
//   - 注意: 序列化时, 堆栈信息无效做丢弃处理
func (e *Error) UnmarshalJSON(data []byte) error {
	em := errorMarshal{}
	if err := packageJsoner.Unmarshal(data, &em); err != nil {
		return err
	}
	e.code = em.Code
	e.msg = em.Msg
	e.err = nil
	e.isDefined = false
	e.caller = nil
	if em.Cause != nil {
		e.errorUnmarshal(em.Cause, e)
	}
	return nil
}
func (e *Error) errorUnmarshal(errorLinked *errorMarshal, err *Error) {
	err.err = newGerror(errorLinked.Code, errorLinked.Msg, nil, false, false)
	if errorLinked.Cause != nil {
		e.errorUnmarshal(errorLinked.Cause, err.err.(*Error))
	}
}

// Error 标准库接口方法引入
func Unwrap(err error) error {
	if err == nil {
		return nil
	}
	u, ok := err.(interface {
		Unwrap() error
	})
	if !ok {
		return nil
	}
	return u.Unwrap()
}

// Is 方法判断被包装的 error 是否是包含指定target错误
//   - 检查 error 错误链中是否存在与 target 匹配的错误
//   - 用于确定错误是否为某个特定的预定义错误值
//   - 类似于 == 比较，但能穿透错误包装
//   - 返回 true: 表示错误链中包含了目标错误
//   - 返回 false: 表示未找到目标错误
func Is(err, target error) bool {
	return errors.Is(err, target)
}

// As 尝试将错误转换为特定的错误类型（错误类型转换）——基于类型断言机制/类型选择机制
//   - 尝试将 error 或其包装的任何错误转换为 target 指向的类型
//   - 用于获取特定类型的错误以访问其方法或字段
//   - 类似于类型断言 error.(T)，但能穿透错误包装
//   - 返回 true: 表示成功找到匹配的错误类型，并已将值存入 target
//   - 返回 false: 表示未找到匹配的错误类型
func As(err error, target any) bool {
	return errors.As(err, target)
}

// Join 合并多个错误为一个错误
func Join(errs ...error) error {
	return errors.Join(errs...)
}
